<?php

namespace Tictactoe\Ai;

use Tictactoe\Game as Game;

class Estimate {

    protected $limit = 5;
    protected $lastMove = array();

    /**
     *
     * @var Game
     */
    protected $game = null;
    protected $lastX = 0;
    protected $lastY = 0;

    protected $oponentOwnerFlag = false;


    /**
     * @param \Tictactoe\Game $game
     */
    public function __construct(Game $game, $lastX, $lastY, $oponentOwnerFlag = false) {
        $this->setGame($game);
        $this->setLastX($lastX);
        $this->setLastY($lastY);
        $this->setOponentOwnerFlag($oponentOwnerFlag);
    }

    public function getOponentOwnerFlag() {
        return $this->oponentOwnerFlag;
    }

    public function setOponentOwnerFlag($oponentOwnerFlag) {
        $this->oponentOwnerFlag = $oponentOwnerFlag;
    }

        
    public function getLastX() {
        return $this->lastX;
    }

    public function getLastY() {
        return $this->lastY;
    }

    public function setLastX($lastX) {
        $this->lastX = (int) $lastX;
    }

    public function setLastY($lastY) {
        $this->lastY = (int) $lastY;
    }

    /**
     * @return Game
     */
    public function getGame() {
        return $this->game;
    }

    /**
     * @param Game $game
     */
    public function setGame(Game $game) {
        $this->game = $game;
    }

    /**
     * ocenia aktualny stan gry dla podanej wartosci
     * zwraca licze, im wieksza liczba tym lepszy rozklad
     * ruchow dla podanej wartosci v
     * 
     * @param Game $game
     * @param int $v
     * @return int
     */
    public function getEstimateForValue($v) {

        $points = 0;
        $size = $this->getGame()->getSize();


        /**
         * horizontal
         */
        for ($x = 0; $x < $size; $x++) {

            $this->clearMove();

            for ($y = 0; $y < $size; $y++) {
                $value = $this->getGame()->getValue($x, $y);
                $this->addMove($x, $y, $value);
                $points += $this->calcPoints($v);
            }
        }


        /**
         * vertical
         */
        for ($y = 0; $y < $size; $y++) {

            $this->clearMove();

            for ($x = 0; $x < $size; $x++) {
                $value = $this->getGame()->getValue($x, $y);
                $this->addMove($x, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        /**
         * left to right cross JEST JUZ OK
         */
        for ($x = 0; $x < $size; $x++) {

            $this->clearMove();

            // petla LR_1
            // analizuje srodkowa przekatna
            for ($y = 0; $y < $size; $y++) {

                $xx = $y + $x;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }

            $this->clearMove();

            // petla LR_2
            for ($y = 0; $y < $size; $y++) {

                // warunek zeby dwa razy nie analizowac srodkowej przekatnej
                // ta srodkowa przekatna jest analizowana przez petle LR_1
                if ($x == 0) {
                    $this->clearMove();
                    continue;
                }

                $xx = $y - $x;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        /**
         * right to left cross JEST JUZ OK
         */
        for ($x = $size - 1; $x >= 0; $x--) {

            $this->clearMove();

            // petla RL_1
            // analizuje srodkowa przekatna
            for ($y = 0; $y < $size; $y++) {

                $xx = $x - $y;

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }

            $this->clearMove();

            // petla RL_2
            for ($y = 0; $y < $size; $y++) {

                // warunek zeby dwa razy nie analizowac srodkowej przekatnej
                // ta srodkowa przekatna jest analizowana przez petle RL_1
                if ($x == $size - 1) {
                    $this->clearMove();
                    continue;
                }

                // metoda prob i bledow
                $xx = $x - $y + 2 * ($size - 1 - $x);

                $value = $this->getGame()->getValue($xx, $y);
                if ($value === null) {
                    $this->clearMove();
                    continue;
                }
                $this->addMove($xx, $y, $value);
                $points += $this->calcPoints($v);
            }
        }

        return $points;
    }

    protected function addMove($x, $y, $valueXY) {
        $this->lastMove[] = array(
            'x' => $x,
            'y' => $y,
            'val' => $valueXY
        );
        if (count($this->lastMove) > $this->limit) {
            $this->lastMove = array_slice($this->lastMove, 1);
        }
    }

    protected function calcPoints($value) {

        /**
         * jak w pieciu komorkach tablicy lastMoves 
         * jest jakis znak o wartosci value
         * a reszta znakow to wartosc 0
         * to zacznij punktowac
         * 100 = 5 x value
         * 80 = 4 x value
         * 50 = 3 x value
         * 30 = 2 x value
         * 10 = 1 x value
         */
        /**
         * czy ostatni ruch jest w tablicy analizowanych ruchow wczesniejszych
         */
        $lastMoveInArray = false;

        // pkt 1, czy sa tylko znaki value i znak 0
        $calc = 0;
        $qty = count($this->lastMove);
        if ($qty === $this->limit) {
            for ($i = 0; $i < $qty; $i++) {
                $last = $this->lastMove[$i];
                if ($last['val'] == 0 || $last['val'] == $value) {
                    if ($last['val'] == $value) {
                        if ($last['x'] == $this->getLastX() && $last['y'] == $this->getLastY()) {
                            $lastMoveInArray = true;
                        }
                        $calc++;
                    }
                } else {
                    $calc = 0;
                    break;
                }
            }
        }


        if ($calc == 5) {

            /**
             * throw exception when winner exists
             */
            $e = new Exception\EstimateWinner('Winner exists !');
            $e->setForValue($value);
            foreach ($this->lastMove as $one) {
                $e->addMove($one['x'], $one['y']);
            }
            throw $e;
        }

        if ($calc == 4) {

            /**
             * dosc zawiły warunek:
             * 1. jezeli ostatni ruch spowodowal ze powstaly 4 symbole 
             * 2. jezeli jest to populacja ruchow od przeciwnika
             * wtedy przerwij i w trybie pilnym poinformuja klase Move
             */
            if ($lastMoveInArray && $this->getOponentOwnerFlag()) {

                /**
                 * throw exception when winner exists
                 */
                $e = new Exception\EstimateOponentAlmostWin('Almost winner exists !');
                $e->setForValue($value);
                foreach ($this->lastMove as $one) {
                    $e->addMove($one['x'], $one['y']);
                }
                throw $e;
            }
            
            return 1000;
        }

        if ($calc == 3) {
            return 400;
        }

        if ($calc == 2) {
            return 50;
        }

        if ($calc == 1) {
            return 10;
        }

        return 0;
    }

    protected function clearMove() {
        $this->lastMove = array();
    }

}
